home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 002 / bigsort.arc / BIGSORT.PAS < prev   
Pascal/Delphi Source File  |  1985-09-22  |  15KB  |  461 lines

  1.   {$C-}
  2.   {$G512}
  3.   {$P512}
  4.  
  5. PROGRAM bigsort(Input, Output);
  6.  
  7.     {*************************************************************************}
  8.     {*         Copyright (c) Kim Kokkonen, TurboPower Software, 1985         *}
  9.     {*  Released to the public domain for personal, non-commercial use only  *}
  10.     {*************************************************************************}
  11.  
  12.     { sort as large a text file as fits in memory                             }
  13.     { designed as a filter, requires Turbo Pascal 3.0 to compile              }
  14.     { written 7/85, phone 408-378-3672                                        }
  15.     { see options in WRITEHELP, call BIGSORT with no arguments to list options}
  16.     { sorts more than 3x faster than MSDOS SORT for large files               }
  17.     { includes a RANDOMIZE feature that aids in sorting presorted files       }
  18.     { compile with maximum heap size A000                                     }
  19.  
  20.   CONST
  21.     maxfile = 15000;          {max number of lines in input file.
  22.                               limited by 4*maxfile<65000}
  23.     stackparas = 512;         {minimum paragraphs to reserve for stack during read-in}
  24.     ss = 9;                   {sort switchover from quick to bubble}
  25.     toklen = 64;              {max length of a command line token}
  26.     maxtok = 4;               {max number of tokens on command line}
  27.     optiondelim = '-';        {char used to introduce command line options}
  28.  
  29.   TYPE
  30.     linebuf = STRING[255];
  31.     lineptr = ^byte;
  32.     linearray = ARRAY[1..maxfile] OF lineptr;
  33.  
  34.   VAR
  35.     lines : linearray;        {pointers to each text line stored here}
  36.     nlines : Integer;         {number of lines}
  37.     showstats, status, partial, upper, reverse : Boolean; {option flags}
  38.     numtocopy, begincol, endcol : Integer; {option values}
  39.     reg : RECORD              {register variable}
  40.             ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer;
  41.           END;
  42.  
  43.   PROCEDURE breakhalt;
  44.       {-executed when break is detected}
  45.       {exit with return code 1}
  46.     BEGIN
  47.       INLINE(
  48.         $B8/$01/$4C/          {mov ax,4c01}
  49.         $CD/$21               {int 21}
  50.         );
  51.     END;                      {breakhalt}
  52.  
  53.   PROCEDURE setbreak;
  54.       {-set the ctrl-break address to a process exit handler}
  55.     BEGIN
  56.       reg.ax := $2523;
  57.       reg.ds := CSeg;
  58.       reg.dx := Ofs(breakhalt);
  59.       MsDos(reg);
  60.     END;                      {setbreak}
  61.  
  62.   PROCEDURE checkkeys;
  63.       {-capture ^C, ^S, ^Q}
  64.       {note that just calling keypressed should trigger int 23 on control-break}
  65.     VAR
  66.       c : Char;
  67.     BEGIN
  68.       WHILE KeyPressed DO BEGIN
  69.         Read(Kbd, c);
  70.         IF c = ^S THEN
  71.           REPEAT
  72.             Read(Kbd, c);
  73.             IF c = ^C THEN breakhalt;
  74.           UNTIL c = ^Q
  75.         ELSE IF c = ^C THEN
  76.           breakhalt;
  77.       END;
  78.     END;                      {checkkeys}
  79.  
  80.   FUNCTION iostat(bit : Integer) : Boolean;
  81.       {-check status of the standard I/O}
  82.       {bit=0 for input, 1 for output}
  83.       {returns true if I or O is through console}
  84.     BEGIN
  85.       reg.ax := $4400;
  86.       reg.bx := bit;          {standard input or output}
  87.       MsDos(reg);
  88.       iostat := ((reg.dx AND 1) <> 0);
  89.     END;                      {iostat}
  90.  
  91.   PROCEDURE putline(VAR l : linebuf; VAR lptr : lineptr);
  92.       {-store a string in a contiguous array in the heap}
  93.       {increment the position cursor nextpos}
  94.       {halt if the string won't fit}
  95.     VAR
  96.       len : Byte ABSOLUTE l;
  97.       tlen : Byte;
  98.       space : Integer;
  99.     BEGIN
  100.       tlen := len+1;          {length of string including length byte}
  101.       space := MaxAvail;
  102.       IF (space < 0) OR ((space-stackparas) > (1+(tlen SHR 4))) THEN BEGIN
  103.         {enough space left to add string}
  104.         GetMem(lptr, tlen);
  105.         Move(l, lptr^, tlen);
  106.       END ELSE BEGIN
  107.         WriteLn(Con);
  108.         WriteLn(Con, 'not enough memory left to store text....');
  109.         Halt;
  110.       END;
  111.     END;                      {putline}
  112.  
  113.   FUNCTION getline(lptr : lineptr) : linebuf;
  114.       {-get a string back from the contiguous heap array}
  115.     VAR
  116.       bytestomove : Byte;
  117.       l : linebuf;
  118.     BEGIN
  119.       bytestomove := lptr^+1;
  120.       Move(lptr^, l, bytestomove);
  121.       getline := l;
  122.     END;                      {getline}
  123.  
  124.   PROCEDURE readinfile(VAR nlines : Integer);
  125.       {-read lines from standard input and put the text on the heap}
  126.     VAR
  127.       l : linebuf;
  128.     BEGIN
  129.       nlines := 0;
  130.       IF status THEN BEGIN
  131.         Write(Con, ^M); ClrEol;
  132.         Write(Con, 'READING       ');
  133.       END;
  134.       WHILE NOT EoF DO BEGIN
  135.         {read line}
  136.         ReadLn(l);
  137.         IF nlines < maxfile THEN BEGIN
  138.           nlines := nlines+1;
  139.           IF status AND (nlines AND 31 = 0) THEN
  140.             Write(Con, ^H^H^H^H^H, nlines:5);
  141.           checkkeys;
  142.           {store pointer into text heap}
  143.           {store line on text heap}
  144.           putline(l, lines[nlines]);
  145.         END ELSE BEGIN
  146.           WriteLn(Con);
  147.           WriteLn(Con, 'Exceeded maximum number of lines....');
  148.           Halt;
  149.         END;
  150.       END;
  151.     END;                      {readinfile}
  152.  
  153.   PROCEDURE writeoutfile(nlines : Integer);
  154.       {-write the sorted information out}
  155.     VAR
  156.       i : Integer;
  157.       l : linebuf;
  158.     BEGIN
  159.       IF showstats THEN Write(Con, 'WRITING       ');
  160.       FOR i := 1 TO nlines DO BEGIN
  161.         {for unknown reason, cannot put getline inside of writeln for DOS 2.1}
  162.         l := getline(lines[i]);
  163.         WriteLn(l);
  164.         IF showstats AND (i MOD 32 = 0) THEN
  165.           Write(Con, ^H^H^H^H^H, i:5);
  166.         checkkeys;
  167.       END;
  168.       IF showstats THEN BEGIN
  169.         Write(Con, ^M); ClrEol;
  170.       END;
  171.     END;                      {writeoutfile}
  172.  
  173.   PROCEDURE Swap(VAR x, y : lineptr);
  174.       {-swap two array pointers}
  175.     VAR
  176.       temp : lineptr;
  177.     BEGIN
  178.       temp := x;
  179.       x := y;
  180.       y := temp;
  181.     END;                      {swap}
  182.  
  183.   PROCEDURE mixlines(nlines : Integer);
  184.       {-randomize record order to aid quicksort with semi-presorted lists}
  185.     VAR
  186.       i : Integer;
  187.     BEGIN
  188.       FOR i := 1 TO nlines DO
  189.         Swap(lines[i], lines[1+Random(nlines)]);
  190.     END;                      {mixlines}
  191.  
  192.   PROCEDURE stupcase(VAR l : linebuf);
  193.       {-return uppercase of a string}
  194.     VAR
  195.       i : Byte;
  196.     BEGIN
  197.       FOR i := 1 TO Length(l) DO l[i] := UpCase(l[i]);
  198.     END;                      {stupcase}
  199.  
  200.   FUNCTION lessthan(l1, l2 : linebuf) : Boolean;
  201.       {-return true if l1<l2 under the option assumptions}
  202.     BEGIN
  203.       IF upper THEN BEGIN
  204.         stupcase(l1);
  205.         stupcase(l2);
  206.       END;
  207.       IF partial THEN BEGIN
  208.         l1 := Copy(l1, begincol, numtocopy);
  209.         l2 := Copy(l2, begincol, numtocopy);
  210.       END;
  211.       IF reverse THEN
  212.         lessthan := (l1 > l2)
  213.       ELSE
  214.         lessthan := (l1 < l2)
  215.     END;                      {lessthan}
  216.  
  217.   FUNCTION equal(l1, l2 : linebuf) : Boolean;
  218.       {-return true if l1=l2 under the option assumptions}
  219.     BEGIN
  220.       IF upper THEN BEGIN
  221.         stupcase(l1);
  222.         stupcase(l2);
  223.       END;
  224.       IF partial THEN BEGIN
  225.         l1 := Copy(l1, begincol, numtocopy);
  226.         l2 := Copy(l2, begincol, numtocopy);
  227.       END;
  228.       equal := (l1 = l2);
  229.     END;                      {equal}
  230.  
  231.   PROCEDURE bubblesort(k, l : Integer);
  232.       {-simple n**2 sort good for short lists}
  233.     VAR
  234.       i, j : Integer;
  235.     BEGIN
  236.       FOR i := k TO (l-1) DO
  237.         FOR j := l DOWNTO (i+1) DO
  238.           IF lessthan(getline(lines[j]), getline(lines[j-1])) THEN
  239.             Swap(lines[j], lines[j-1]);
  240.     END;                      {bubblesort}
  241.  
  242.   PROCEDURE quicksort(i, j : Integer);
  243.       {-fast sorting algorithm modified to be hybrid with bubblesort}
  244.     VAR
  245.       pivot : linebuf;
  246.       k, pivotindex, ramleft : Integer;
  247.       enoughram : Boolean;
  248.  
  249.     PROCEDURE writestatus(i, j : Integer);
  250.         {-provide some reassurance that sort is proceeding}
  251.       BEGIN
  252.         Write(Con, ^H^H^H^H^H); ClrEol;
  253.         {prints size of current partition being sorted}
  254.         Write(Con, (j-i):5);
  255.       END;                    {writestatus}
  256.  
  257.     FUNCTION findpivot(i, j : Integer) : Integer;
  258.         {-part of quicksort}
  259.       VAR
  260.         firstkey, l : linebuf;
  261.         k : Integer;
  262.       BEGIN
  263.         firstkey := getline(lines[i]);
  264.         FOR k := (i+1) TO j DO BEGIN
  265.           l := getline(lines[k]);
  266.           IF lessthan(l, firstkey) THEN BEGIN
  267.             findpivot := i;
  268.             Exit;
  269.           END ELSE IF NOT(equal(l, firstkey)) THEN BEGIN
  270.             findpivot := k;
  271.             Exit;
  272.           END;
  273.         END;
  274.         findpivot := 0;
  275.       END;                    {findpivot}
  276.  
  277.     FUNCTION partition(i, j : Integer; VAR pivot : linebuf) : Integer;
  278.         {-part of quicksort}
  279.       VAR
  280.         l, r : Integer;
  281.       BEGIN
  282.         l := i;
  283.         r := j;
  284.         REPEAT
  285.           Swap(lines[l], lines[r]);
  286.           WHILE lessthan(getline(lines[l]), pivot) DO l := l+1;
  287.           WHILE NOT(lessthan(getline(lines[r]), pivot)) DO r := r-1;
  288.         UNTIL l > r;
  289.         partition := l;
  290.       END;                    {partition}
  291.  
  292.     BEGIN                     {quicksort}
  293.       checkkeys;              {check for a break}
  294.       IF status THEN writestatus(i, j);
  295.       pivotindex := findpivot(i, j);
  296.       IF pivotindex <> 0 THEN BEGIN
  297.         pivot := getline(lines[pivotindex]);
  298.         k := partition(i, j, pivot);
  299.         ramleft := MemAvail;
  300.         enoughram := (ramleft < 0) OR (ramleft > 32);
  301.         IF ((k-1-i) > ss) AND enoughram THEN
  302.           quicksort(i, k-1)
  303.         ELSE
  304.           bubblesort(i, k-1);
  305.         IF ((j-k) > ss) AND enoughram THEN
  306.           quicksort(k, j)
  307.         ELSE
  308.           bubblesort(k, j);
  309.       END;
  310.     END;                      {quicksort}
  311.  
  312.   PROCEDURE writehelp;
  313.       {-display a help screen}
  314.     BEGIN
  315.       WriteLn(Con);
  316.       WriteLn(Con, 'Usage: BIGSORT [Options] <InputPathname [>OutputPathName]');
  317.       LowVideo;
  318.       WriteLn(Con);
  319.       WriteLn(Con, 'Sort limited in size only by available RAM.');
  320.       WriteLn(Con, '384K free RAM will sort a 256K file of 7000 lines.');
  321.       WriteLn(Con, 'Each text line limited to 255 characters and must be terminated by a <CR><LF>.');
  322.       WriteLn(Con, 'Maximum of 15000 text lines.');
  323.       WriteLn(Con, 'Input text is automatically randomized to avoid presorting problems.');
  324.       WriteLn(Con);
  325.       NormVideo;
  326.       WriteLn(Con, 'Options:');
  327.       LowVideo;
  328.       WriteLn(Con, '    -I      Ignore case while sorting');
  329.       WriteLn(Con, '    -R      sort in Reverse order');
  330.       WriteLn(Con, '    -Bn     Begin sort key with column n of each line (default 1)');
  331.       WriteLn(Con, '    -En     End sort key with column n of each line (default end of line)');
  332.       WriteLn(Con, '    -Q      Quiet mode. No status during sort');
  333.       NormVideo;
  334.     END;                      {writehelp}
  335.  
  336.   PROCEDURE setoptions;
  337.       {-analyze input line}
  338.     VAR
  339.       i, code : Integer;
  340.       num : STRING[6];
  341.       arg : STRING[64];
  342.     BEGIN
  343.       {set defaults}
  344.       upper := False; reverse := False; status := True;
  345.       begincol := 1; endcol := 255; partial := False;
  346.  
  347.       WriteLn(Con);
  348.  
  349.       {scan through argument list}
  350.       i := 1;
  351.       WHILE i <= ParamCount DO BEGIN
  352.         arg := ParamStr(i);
  353.         IF (arg[1] = optiondelim) AND (Length(arg) > 1) THEN BEGIN
  354.           CASE UpCase(arg[2]) OF
  355.             'I' : upper := True;
  356.             'R' : reverse := True;
  357.             'Q' : status := False;
  358.             'B' : BEGIN
  359.                     num := Copy(arg, 3, 6);
  360.                     Val(num, begincol, code);
  361.                     IF code <> 0 THEN BEGIN
  362.                       WriteLn(Con, 'Illegal Begin column specified: ', arg);
  363.                       writehelp;
  364.                       Halt;
  365.                     END;
  366.                     IF (begincol <= 0) OR (begincol > 255) THEN BEGIN
  367.                       WriteLn(Con, 'Illegal Begin column specified: ', arg);
  368.                       WriteLn(Con, ' column must be in the range of 1..255');
  369.                       writehelp;
  370.                       Halt;
  371.                     END;
  372.                     IF begincol > 1 THEN partial := True;
  373.                   END;
  374.             'E' : BEGIN
  375.                     num := Copy(arg, 3, 6);
  376.                     Val(num, endcol, code);
  377.                     IF code <> 0 THEN BEGIN
  378.                       WriteLn(Con, 'Illegal End column specified: ', arg);
  379.                       writehelp;
  380.                       Halt;
  381.                     END;
  382.                     IF (endcol <= 0) OR (endcol > 255) THEN BEGIN
  383.                       WriteLn(Con, 'Illegal End column specified: ', arg);
  384.                       WriteLn(Con, ' column must be in the range of 1..255');
  385.                       writehelp;
  386.                       Halt;
  387.                     END;
  388.                     IF endcol < 255 THEN partial := True;
  389.                   END;
  390.           ELSE
  391.             WriteLn(Con, 'Unrecognized command line option: ', arg);
  392.             writehelp;
  393.             Halt;
  394.           END;
  395.         END ELSE BEGIN
  396.           WriteLn(Con, 'Unrecognized command line option: ', arg);
  397.           writehelp;
  398.           Halt;
  399.         END;
  400.         i := i+1;
  401.       END;
  402.       numtocopy := endcol-begincol+1;
  403.       showstats := status AND NOT(iostat(1));
  404.     END;                      {setoptions}
  405.  
  406.   FUNCTION ramavail : Real;
  407.       {-return the number of bytes of RAM left for heap and stack}
  408.     VAR
  409.       t : Real;
  410.     BEGIN
  411.       t := MaxAvail;
  412.       IF t < 0 THEN t := 65536.0+t;
  413.       ramavail := 16.0*t;
  414.     END;                      {ramavail}
  415.  
  416.   BEGIN                       {main}
  417.  
  418.     IF iostat(0) THEN BEGIN
  419.       WriteLn(Con);
  420.       WriteLn(Con, 'input must be redirected from a file....');
  421.       writehelp;
  422.       Halt;
  423.     END;
  424.  
  425.     {analyze command line options}
  426.     setoptions;
  427.  
  428.     {make sure we can break out}
  429.     setbreak;
  430.  
  431.     IF status THEN
  432.       WriteLn(Con, 'Total RAM for heap and stack: ', ramavail:6:0);
  433.  
  434.     {read in the input file}
  435.     readinfile(nlines);
  436.  
  437.     IF status THEN BEGIN
  438.       Write(Con, ^M); ClrEol;
  439.       WriteLn(Con, 'RAM left for recursion stack: ', ramavail:6:0);
  440.       WriteLn(Con, 'Total lines: ', nlines);
  441.     END;
  442.  
  443.     {randomize records}
  444.     IF status THEN Write(Con, 'RANDOMIZING');
  445.     mixlines(nlines);
  446.  
  447.     {sort}
  448.     IF status THEN BEGIN
  449.       Write(Con, ^M); ClrEol;
  450.       Write(Con, 'SORTING       ');
  451.     END;
  452.     IF nlines > ss THEN quicksort(1, nlines) ELSE bubblesort(1, nlines);
  453.     IF status THEN BEGIN
  454.       Write(Con, ^M); ClrEol;
  455.     END;
  456.  
  457.     {write out the results}
  458.     writeoutfile(nlines);
  459.  
  460.   END.
  461.